Racket:一门用于创造新语言的语言
在本文中,我们将探讨 Racket——一门专为快速创建全新编程语言而生的编程语言。终于,有一个叫“Racket”的语言,值得你投入时间。
Racket 最初是一个教学工具,但如今已发展成为一个复杂而高效的开发平台。它不仅是一门语言,更是一个由各种工具组成的生态系统,且拥有极为出色的文档支持。
什么是 Racket?
Racket 是一个结构严谨、理论基础扎实的复杂项目,甚至有自己的宣言(manifesto)。它是二十多年实践经验的结晶。
它最初被设计为帮助学生学习编程语言的工具。从这个角度来看,以 Scheme 作为 Racket 的基础可能是个糟糕的选择——但恰恰是这种选择推动了项目的演进,正如其宣言中所说:
“我们意识到,不可能在一小时内教会 Scheme,然后专注于计算和编程的本质。”
最终,这个项目演变成了我们今天所知的 Racket。
Racket 的创造者们相信:编程的本质在于用合适的语言解决问题。因此,Racket 的核心使命就是——快速创建新的编程语言。
Racket 不只是一门语言
Racket 拥有一整套编程语言家族,这些语言可以安全地相互连接。它既包含保护机制,允许安全使用底层(比如类似 C 的)语言特性,也支持更高层次的语言抽象。
它还自带一个集成开发环境:Dr. Racket,这个 IDE 被设计为语言开发流程中不可或缺的一部分,具备资源管理功能,有助于高效设计新语言。当然,它也支持其他主流 IDE,如 VS Code 或 Emacs。
总而言之,Racket 是一套完整、协调的工具集合,为编程语言的设计提供一体化体验。请注意,它不是一个“语言工作台”(language workbench):它不会帮你自动生成编辑器或配套工具。相反,它是围绕语言创建与执行而构建的一整套独立工具的生态系统。
使用 Racket 的优势在于:你可以从零开始,借助其详尽而丰富的文档,最终构建出一整套语言系统。缺点则是:由于其高度集成的设计环境,你可能需要“从头造轮子”。你很难复用已有的工具或熟悉的开发环境。Racket 就像是一个自成一体的小世界。例如,它拥有自己的构建工具,能够管理从包安装到编译成可执行文件的全过程。
尽管起源于学术界,Racket 已在一些重要项目中得到应用。比如,保罗·格雷厄姆(Paul Graham)用来构建 Hacker News 的编程语言 Arc 就是基于 Racket 开发的。此外,索尼旗下游戏开发商 Naughty Dog 也曾使用 Racket 为其部分游戏开发脚本语言。
学习 Racket
考虑到它的教学起源,Racket 拥有优秀的文档并不令人意外。但真正令人惊讶的是,这些文档极其详尽和深入。仅在其官网上,你就能找到教程、完整指南、参考手册,以及针对系统编程、Web 开发等特定领域的专项指南。
当然,还有关于 DrRacket、包管理系统及其他工具的文档,以及数以百计的 Racket 库说明。他们的文档页面列出了 994 份不同的手册——多到我不得不写个脚本去数。
不仅如此,如果你需要更正式的学习资料,还可以找到一系列书籍,教你:
- 如何用 Racket 以不同方式学习编程
- 如何设计程序(虽然基于 Racket,但具有普遍适用性)
- 各种层次的计算机科学教材,使用 Racket 来讲解基础概念
此外,还有课程、暑期学校和培训项目可供学习 Racket。
Racket 在过去二十年里并未席卷全球,但它已经建立起一个稳定而活跃的社区。这个社区能够帮助你利用 Racket 实现自己的目标。
最好的入门方式,当然是从 Racket 的快速入门指南开始。
Racket 的强大之处
关于 Racket 的资料如此丰富完整,以至于很难再补充什么新内容。事实上,资料多到你可能不知道该从哪里开始。我们认为,提供一个清晰的入门介绍,才能真正帮助你理解 Racket。我们将向你展示使用 Racket 的实际体验,让你判断它是否适合你。
首先要理解的关键一点是:Racket 基于 Scheme。这一点从代码中大量使用括号就可以立刻看出来:
(define (my-length lst)
(cond
[(empty? lst) 0]
[else (+ 1 (my-length (rest lst)))]))
提到 Scheme,所有人第一反应可能都是:括号太多了。至于其他方面,大多数人并不了解。事实上,我认为 Scheme 正是“小众语言”与“主流语言”之间的完美结合。它是一门著名、有影响力且受人尊敬的语言,却又仿佛生活在另一个世界。它的语法复杂,看起来与 C 或 Python 等主流语言家族格格不入。而语法只是表象,其背后的编程哲学更是与你习惯的大相径庭。
Scheme(以及 Racket)的核心理念是:只提供简单的工具,然后让你自由发挥。正因如此,它既复杂又强大。Scheme 中没有语法糖,但却蕴含着巨大的力量。
Racket 能做的惊人之事
Racket 提供了完整的元编程能力。这意味着你可以将代码本身当作数据来操作。在 Racket 中,你可以改变代码的解析方式,实现大多数其他语言无法做到的功能。
例如,下面是一个合法的 Racket 程序:
#lang honu
function fib(n) {
if (n == 0)
0
else if (n == 1)
1
else
fib(n-1) + fib(n-2)
}
fib(30)
第一行 #lang honu
设置了语言模式,即解释后续代码的规则。Honu 只是某个 Racket 用户创建的语言,它允许你用类似 C 的风格编写代码。如你所见,这段代码完全不像传统的 Scheme 程序。
它拥有 Lisp 的灵活性和强大功能,同时还能让你干预语法本身。例如,你可以让语言看起来完全不同,甚至完全摆脱括号的“视觉轰炸”。
这使得 Racket 成为创建技术性领域特定语言(DSL) 的理想工具。你可以用 Racket 创建任何语言,甚至包括用于出版的标记语言,比如 Pollen:书本身就是一段程序。
Racket 的世界
你可以用 Racket 创建各种程序。它自带大量库,支持网络、解析、3D 图形等常见需求。也有许多实际工具是用 Racket 构建的,比如一个用于将本地目录与 AWS S3 存储桶同步的工具。
这一切都很棒——只要你完全处于 Racket 的生态系统之内。一旦你跳出这个生态,就可能遇到问题。
举个例子,先看好的一面。下面是在 Dr. Racket 中运行的一个示例程序:
这个 IDE 看起来不算特别专业,但非常适合学习。它提供交互式帮助,让你轻松理解代码运行过程和每个函数的作用。你拥有一个简单易用的 IDE 和内置解释器。还能要求更多吗?
最终结果是一幅漂亮的棋盘图像。Racket 看起来既强大又易用。
现在,看看同样的代码在 Visual Studio Code 中使用 Racket 扩展运行时会发生什么?你能看出区别吗?
这看起来就没那么好了。你根本看不到任何图像。在 Dr. Racket 中完美运行的程序,在其他环境中却完全失效。
这是集成生态系统中常见的问题:当你使用非专为该生态设计的工具时,某些功能可能会“断裂”。当然,不依赖图形化展示的普通程序在其他环境中运行良好。这只是一个小小的缺陷,但对于习惯现代编程语言的开发者来说,同一段代码在不同环境中表现不同,可能会令人感到不适。
抛开这个缺陷,还有一个重要事项需要注意:
Racket 很容易学……
你有没有遇到过有人说:“这东西很容易学,只要读一下这本书就行”?而你心里立刻想:“如果得读一整本书,那怎么能叫容易学?” 你学习 Racket 时也会有这种感觉。
关于 Racket 的方方面面——从语法到背后的计算机科学原理——都有详尽的文档。这不仅源于它的教学背景,也因为你确实需要花大量时间阅读这些文档。里面充满了关于编程和 Racket 的精彩知识,但也有大量你必须掌握、且只能在这里找到的内容。
很多人学习第一门编程语言时,愿意读一本上千页的巨著。学第二门时,可能只需一本小书,了解它与已知语言的差异。之后每学一门新语言,可能只查查参考资料和几篇教程。因为他们已经理解了编程的本质,认为所有语言大同小异。很多人学 JavaScript 就是这么做的。
……但它其实很难
但这种方法对 Racket 行不通。你不能随便加一堆括号,就以为自己懂了 Scheme。Racket 与你熟知的任何语言都大不相同(除非你已经熟悉 Lisp 或 Scheme)。因此,你必须依赖文档才能真正发挥 Racket 的威力。
所以,Racket “容易学”是因为文档丰富;但它“难学”是因为你必须真正去读这些文档。
你不可能在几个周末就掌握 Racket。好消息是,有些人会觉得这很刺激——这是学习一个全新语言家族的绝佳机会。其他人则需要权衡:Racket 的价值是否值得付出如此大的学习成本。
但一旦你掌握了它,你就能用 Racket 做几乎任何事。还有哪种语言能让你彻底改变编译器理解代码的方式?
总结
Racket 是许多事物的集合:一门语言、一个语言家族、一套强大的工具集。所有这一切都由深入详尽的文档支持,引导你在各种项目中前行。这使得它成为为小众社区构建领域特定语言(DSL)的理想工具。
你可以用它创建类似 Prolog 的语言,或类似 Java 的语言,甚至能在同一代码库中混合使用它们。因此,它也是学习编程、深化对计算机科学理解、并进行实验的绝佳选择。